/*******************************************************************/
/* @ All Rights Reserved Copyright (C) 2014 broada,Co.,Ltd.        */
/*                                                                 */
/*  NAME      = frlua_hook.c                                       */
/*  NOTE      =                                                    */
/*  DATE      = 2015/04/21 by yaohl                                */
/*******************************************************************/

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include <errno.h>

#if defined(FRLINUX4) || defined(FRLINUX5)
    extern "C"{
    #include <lua.h>
    #include <lualib.h>
    #include <lauxlib.h>
    }
#else
    #ifdef LIB_APM_LUAJIT
    #include <luajit-2.0/lua.hpp>
    #else
    #include <lua.hpp>
    #endif
#endif

#include <exception>
using namespace std;

#include "frcomm.h"
#include "frmem.h"
#include "frlog.h"

#include "frlua_hook.h"

#define APM_LUA_FUN_INIT            "apm_lua_init"
#define APM_LUA_FUN_EXIT            "apm_lua_exit"
#define APM_LUA_FUN_MODULE          "main_module"
#define APM_LUA_FUN_POST_ANALYZE    "post_analysis"


FR_STATIC void __frlua_error(lua_State* L, int i_err, char **pp_error_msg)
{
    if (i_err==0)
        return;

    const char *p_luaError;
    char arr_errType[256]={0};
    int i_len=0;

    switch(i_err)
    {
        case LUA_ERRSYNTAX://编译时错误
            /*const char *buf = "mylib.myfun()2222";类似这行语句可以引起编译时错误*/
            snprintf(arr_errType,sizeof(arr_errType),"%s","lua语法错误");
            break;
        case LUA_ERRMEM://内存错误
            snprintf(arr_errType,sizeof(arr_errType),"%s","lua内存错误");
            break;
        case LUA_ERRRUN://运行时错误
            /*const char *buf = "my222lib.myfun()";类似这行语句可以引起运行时错误，my222lib实际上不存在这样的库，返回的值是nil*/
            snprintf(arr_errType,sizeof(arr_errType),"%s","lua语法错误");
            break;
        case LUA_YIELD://线程被挂起错误
            snprintf(arr_errType,sizeof(arr_errType),"%s","lua线程错误");
            break;
        case LUA_ERRERR://在进行错误处理时发生错误
            snprintf(arr_errType,sizeof(arr_errType),"%s","lua语法错误");
            break;
        default:
            break;
    }
    p_luaError = lua_tostring(L, -1);//打印错误结果
    FRLOG_WARN_DETAIL("%s:%s", arr_errType, p_luaError);
    if(pp_error_msg)
    {
        char *p_msg=NULL;
        if(p_luaError)
        {
            i_len = strlen(arr_errType)+1+strlen(p_luaError)+1+20;
            p_msg = (char *)frmalloc(i_len);
            if(p_msg)
                snprintf(p_msg, i_len, "%s\n错误原因:%s", arr_errType, p_luaError);
        }
        else
        {
            i_len = strlen(arr_errType)+1;
            p_msg = (char *)frmalloc(i_len);
            if(p_msg)
                snprintf(p_msg, i_len, "%s", arr_errType);
        }

        if(p_msg)
            *pp_error_msg = p_msg;
    }
    lua_pop(L, 1);

    return;
}

lua_State *frlua_create_lua_stat(const char *p_lua, const char *p_file)
{
    lua_State *p_lua_handle = NULL;
    int i_ret_fun=0;

    if(p_lua == NULL)
        return NULL;

    //创建lua运行环境
    p_lua_handle = luaL_newstate();
    if(p_lua_handle == NULL)
    {
        FRLOG_WARN_DETAIL("luaL_newstate err");
        return NULL;
    }

    luaL_openlibs(p_lua_handle);

    //加载lua脚本文件
    i_ret_fun = luaL_loadstring(p_lua_handle, p_lua);
    if ( i_ret_fun != 0 )
    {
        if(p_file && *p_file != '\0')
            i_ret_fun = luaL_loadfile(p_lua_handle, p_file);
        if ( i_ret_fun != 0 )
        {
            FRLOG_WARN_DETAIL("luaL_loadfile [%s] err", p_file);
            goto ERROR;
        }
    }
    i_ret_fun = lua_pcall(p_lua_handle,0,0,0);
    if ( i_ret_fun != 0 )
    {
        FRLOG_WARN_DETAIL("lua_pcall lua[%s] err", p_file);
        __frlua_error(p_lua_handle,i_ret_fun, NULL);
        goto ERROR;
    }

    return p_lua_handle;

ERROR:
    lua_close(p_lua_handle);
    return NULL;
}

int frlua_check_lua(const char *p_lua, char **pp_error_msg)
{
    lua_State *p_lua_handle = NULL;
    int i_ret_fun=0;

    if(p_lua == NULL)
        return FR_FAIL;

    //创建lua运行环境
    p_lua_handle = luaL_newstate();
    if(p_lua_handle == NULL)
    {
        FRLOG_WARN_DETAIL("luaL_newstate err");
        return FR_FAIL;
    }

    luaL_openlibs(p_lua_handle);

    //加载lua脚本文件
    i_ret_fun = luaL_loadstring(p_lua_handle, p_lua);
    if ( i_ret_fun != 0 )
    {
        __frlua_error(p_lua_handle,i_ret_fun, pp_error_msg);
        goto ERROR;
    }

    i_ret_fun = lua_pcall(p_lua_handle,0,0,0);
    if ( i_ret_fun != 0 )
    {
        __frlua_error(p_lua_handle,i_ret_fun, pp_error_msg);
        goto ERROR;
    }

    lua_close(p_lua_handle);
    return FR_SUCC;

ERROR:
    lua_close(p_lua_handle);
    return FR_FAIL;
}

/*******************************************************************************
*  FUNC     :  lua hook
*  ARGS     :  p_st_lua_params          (lua需要的参数)
            :  p_st_analyzefun          (协议解析模块提供的函数一览)
*  RTN      :  FR_SUCC:成功;FR_FAIL:失败
*  NOTE     :
*******************************************************************************/
FR_STATIC int __frlua_do_hook_script(const char *p_lua
                                   , const char *p_file
                                   , Frlua_params *p_st_lua_params
                                   , Frlua_analyzefun *p_st_analyzefun
                                   , const char *p_fun)
{
    int i_ret_fun = 1, i_ret=FR_FAIL, i_loop=0;
    const char *p_fnName=NULL;
    lua_State *p_lua_handle = NULL;

    if(p_lua == NULL
       || p_st_lua_params == NULL || p_st_lua_params->p_memstat == NULL
       || p_fun == NULL || *p_fun == '\0')
        return FR_FAIL;

    //创建lua运行环境
    p_lua_handle = luaL_newstate();
    if(p_lua_handle == NULL)
    {
        FRLOG_WARN_DETAIL("luaL_newstate err");
        return FR_FAIL;
    }

    luaL_openlibs(p_lua_handle);

    //加载lua脚本文件
    i_ret_fun = luaL_loadstring(p_lua_handle, p_lua);
    if ( i_ret_fun != 0 )
    {
        i_ret_fun = luaL_loadfile(p_lua_handle, p_file);
        if ( i_ret_fun != 0 )
        {
            FRLOG_WARN_DETAIL("luaL_loadfile [%s] err", p_file);
            goto ERROR;
        }
    }
    i_ret_fun = lua_pcall(p_lua_handle,0,0,0);
    if ( i_ret_fun != 0 )
    {
        FRLOG_WARN_DETAIL("lua_pcall lua[%s] err", p_file);
        __frlua_error(p_lua_handle,i_ret_fun, NULL);
        goto ERROR;
    }

    //调用apm_lua_init进行必要的初始化
    lua_getglobal(p_lua_handle, APM_LUA_FUN_INIT);
    lua_pushlightuserdata(p_lua_handle, p_st_lua_params->p_memstat);
    i_ret_fun = lua_pcall(p_lua_handle, 1, 1, 0);
    if ( i_ret_fun != 0 )
    {
        FRLOG_WARN_DETAIL("lua_pcall fun=%s err[%s]", APM_LUA_FUN_INIT, lua_tostring(p_lua_handle,-1));
        i_ret = FR_FAIL;
        goto ERROR;
    }
    i_ret_fun = lua_tointeger(p_lua_handle,-1);
    //frwarn("%s ret=%d\n", APM_LUA_FUN_INIT, i_ret_fun);
    lua_pop(p_lua_handle, 1);

    //设置lua中全局变量
    if(p_st_lua_params->p_st_conn)
    {
        lua_pushlightuserdata(p_lua_handle, p_st_lua_params->p_st_conn);
        lua_setglobal(p_lua_handle, "G_VAR_CT");
    }
    if(p_st_lua_params->p_st_session)
    {
        lua_pushlightuserdata(p_lua_handle, p_st_lua_params->p_st_session);
        lua_setglobal(p_lua_handle, "G_VAR_SESSION");
    }
    if(p_st_lua_params->p_st_capdata)
    {
        lua_pushlightuserdata(p_lua_handle, p_st_lua_params->p_st_capdata);
        lua_setglobal(p_lua_handle, "G_VAR_CAP_DATA");
    }
    if(p_st_lua_params->p_st_report_data)
    {
        lua_pushlightuserdata(p_lua_handle, p_st_lua_params->p_st_report_data);
        lua_setglobal(p_lua_handle, "G_VAR_REPORT_DATA");
    }

    //注册c提供的函数
    for(i_loop=0; p_st_analyzefun && (p_st_analyzefun+i_loop)->p_fn; i_loop++)
    {
        p_fnName = (p_st_analyzefun+i_loop)->p_fnName;
        if( p_fnName != NULL && *p_fnName != '\0')
        {
            lua_pushcfunction(p_lua_handle, (p_st_analyzefun+i_loop)->p_fn);
            lua_setglobal(p_lua_handle, p_fnName);
            FRLOG_DEBUG("regist fun(%s) for lua", p_fnName);
            //frwarn("regist fun(%s) for lua\n", p_fnName);
        }
    }

    //运行lua函数
    lua_getglobal(p_lua_handle, p_fun);
    //lua_pushstring(p_lua_handle, "aaa");
    //i_ret_fun = lua_pcall(p_lua_handle, 1, 1, 0);
    i_ret_fun = lua_pcall(p_lua_handle, 0, 1, 0);
    if ( i_ret_fun != 0 )
    {
        FRLOG_WARN_DETAIL("lua[%s] err:%s", p_file, lua_tostring(p_lua_handle,-1));
        i_ret = FR_FAIL;
        goto ERROR;
    }
    lua_pop(p_lua_handle, 1);

    lua_close(p_lua_handle);
    return FR_SUCC;

ERROR:
    lua_close(p_lua_handle);
    return i_ret;
}

/*******************************************************************************
*  FUNC     :  lua hook
*  ARGS     :  p_st_lua_params          (lua需要的参数)
            :  p_st_analyzefun          (协议解析模块提供的函数一览)
*  RTN      :  FR_NO:保留上报数据;FR_YES:删除该上报数据
*  NOTE     :
*******************************************************************************/
FR_STATIC int __frlua_do_hook_handle(lua_State *p_lua_handle, Frlua_params *p_st_lua_params, Frlua_analyzefun *p_st_analyzefun, const char *p_fun)
{
    int i_ret_fun = 1;
    int i_drop = 0;

    if(p_lua_handle == NULL
       || p_st_lua_params == NULL || p_st_lua_params->p_memstat == NULL
       || p_fun == NULL || *p_fun == '\0')
        return FR_FAIL;

    //调用apm_lua_init进行必要的初始化
    lua_getglobal(p_lua_handle, APM_LUA_FUN_INIT);
    lua_pushlightuserdata(p_lua_handle, p_st_lua_params->p_memstat);
    i_ret_fun = lua_pcall(p_lua_handle, 1, 1, 0);
    if ( i_ret_fun != 0 )
    {
        FRLOG_WARN_DETAIL("lua_pcall fun=%s err[%s]", APM_LUA_FUN_INIT, lua_tostring(p_lua_handle,-1));
        goto ERROR;
    }
    i_ret_fun = lua_tointeger(p_lua_handle,-1);
    //frwarn("%s ret=%d\n", APM_LUA_FUN_INIT, i_ret_fun);
    lua_pop(p_lua_handle, 1);

    //设置lua中全局变量
    //if(p_st_lua_params->p_st_conn)
    //{
    //    lua_pushlightuserdata(p_lua_handle, p_st_lua_params->p_st_conn);
    //    lua_setglobal(p_lua_handle, "G_VAR_CT");
    //}
    //if(p_st_lua_params->p_st_session)
    //{
    //    lua_pushlightuserdata(p_lua_handle, p_st_lua_params->p_st_session);
    //    lua_setglobal(p_lua_handle, "G_VAR_SESSION");
    //}
    //if(p_st_lua_params->p_st_capdata)
    //{
    //    lua_pushlightuserdata(p_lua_handle, p_st_lua_params->p_st_capdata);
    //    lua_setglobal(p_lua_handle, "G_VAR_CAP_DATA");
    //}
    if(p_st_lua_params->p_st_report_data)
    {
        lua_pushlightuserdata(p_lua_handle, p_st_lua_params->p_st_report_data);
        lua_setglobal(p_lua_handle, "G_VAR_REPORT_DATA");
    }

    //运行lua函数
    lua_getglobal(p_lua_handle, p_fun);

    try
    {
        i_ret_fun = lua_pcall(p_lua_handle, 0, 1, 0);//1个返回值

        //获取返回值
        try {  i_drop = lua_tonumber(p_lua_handle, -1); } catch (exception& e11) { i_drop = 0; }
        lua_pop(p_lua_handle, 1);

        if(i_drop != 1 && i_drop != 0) i_drop = 0;
    }
    catch (exception& e1)
    {
        i_drop = 0;
        try
        {
            i_ret_fun = lua_pcall(p_lua_handle, 0, 0, 0);//没有返回值
            if ( i_ret_fun != 0 )
                goto ERROR;
            }
        catch (exception& e21)
        {
            goto ERROR;
        }
    }

    if(i_drop == 1)
        return FR_YES;
    else
        return FR_NO;

ERROR:
    {
        //log里增加当前连接信息
        char arr_l4[128];
        u_short ip_proto = 0;
        u_int src_ip = 0, dst_ip = 0;
        u_short src_port = 0, dst_port =0;
        if(p_st_lua_params->p_st_conn)
        {
            ip_proto = p_st_lua_params->p_st_conn->ip_proto;
            src_ip = p_st_lua_params->p_st_conn->src_ip;
            dst_ip = p_st_lua_params->p_st_conn->dst_ip;
            src_port = p_st_lua_params->p_st_conn->src_port;
            dst_port = p_st_lua_params->p_st_conn->dst_port;
        }
        arr_l4[0] = '\0';
        frl4_print2(arr_l4, sizeof(arr_l4), ip_proto, src_ip, src_port, dst_ip, dst_port);
        FRLOG_WARN_DETAIL("%s fun[%s] err:%s",arr_l4, p_fun, lua_tostring(p_lua_handle,-1));
    }
    return FR_NO;
}

int frlua_do_hook_module(Frlua_params *p_st_lua_params, Frlua_analyzefun *p_st_analyzefun)
{
    FrluaInfo *p_st_luaInfo=NULL;

    if(p_st_lua_params->p_st_conn)
        p_st_luaInfo = &p_st_lua_params->p_st_conn->st_luaInfo;

    if(p_st_luaInfo == NULL)
        return FR_FAIL;

    //return __frlua_do_hook_script(p_st_luaInfo, p_st_lua_params, p_st_analyzefun, APM_LUA_FUN_MODULE);
    return FR_SUCC;
}

/*******************************************************************************
*  FUNC     :  lua hook
*  ARGS     :  p_st_lua_params          (lua需要的参数)
            :  p_st_analyzefun          (协议解析模块提供的函数一览)
*  RTN      :  FR_NO:保留上报数据;FR_YES:删除该上报数据
*  NOTE     :  多线程需要加锁
*******************************************************************************/
int frlua_do_hook_report(void *p_memstat, lua_State *p_lua_handle, Conntrack *p_st_conn, Frreport_data *p_st_report_data)
{
    int i_ret_fun=0;
    Frlua_params st_lua_params={ p_memstat, p_st_conn, NULL, NULL, p_st_report_data };
    Frlua_analyzefun arr_st_analyzefun[]={ {NULL,NULL} };

    i_ret_fun = __frlua_do_hook_handle(p_lua_handle, &st_lua_params, arr_st_analyzefun, APM_LUA_FUN_POST_ANALYZE);
    //frdbg("frlua_do_hook_report end(__frlua_do_hook_script ret=%d)\n", i_ret_fun);

    return i_ret_fun;
}

